/*
 * 础光实时操作系统PhotonRTOS -- AUTOSAR事件实现
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

VAR(struct smp_lock, TYPEDEF) set_event_async_lock;

LOCAL_INLINE FUNC(int32_t, OS_CODE) task_is_extended(
	VAR(int32_t, AUTOMATIC) TaskID)
{
	P2VAR(struct task_desc, AUTOMATIC, OS_APPL_DATA) osk = &osek_tasks[TaskID];

	if ((osk->flags & TF_EXTENDED) != TF_EXTENDED) {
		return 1;
	}

	return 0;
}

/**
 * 调用ClearEvent的扩展任务的事件被清除
 * 这些事件与事件掩码<Mask>相关。
 * 语法：
 *     StatusType ClearEvent ( EventMaskType <Mask> )
 * 参数（输入）：
 *     Mask			要清除的事件掩码
 *  参数（输出）：
 *    无
 * 特殊说明：
 *    系统服务ClearEvent限制为拥有事件的扩展任务。
 * 状态：
 *    标准：
 *        无错误，E_OK
 *    扩展：
 *        调用任务不是扩展任务，E_OS_ACCESS
 *        在中断级调用，E_OS+CALLEVEL
 * 一致性：
 *     ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) ClearEvent(
	VAR(EventMaskType, AUTOMATIC) Mask)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

#if defined(EXTENDED_STATUS)
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)
	/**
	 * 在扩展模式下，检查当前任务是否为扩展任务
	 * 非扩展任务（基本任务）不支持事件，
	 * 也就不能调用本函数
	 */
	if (task_is_extended(osek_current_id())) {
		ret = E_OS_ACCESS;
		goto out;
	}

#endif

	SuspendAllInterrupts();
	osek_tasks[osek_current_id()].event_mask &=
			~(Mask & osek_tasks[osek_current_id()].event_mask);
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(ClearEvent, ret, Mask);
	}

	return ret;
}

/**
 * 该服务返回任务<TaskID>所有事件位的当前状态，
 * 而不是任务当前正在等待的事件。
 * 该服务可以从中断服务例程、
 * 任务级以及某些钩子例程中调用。
 * 语法：
 *     StatusType GetEvent ( TaskType <TaskID>, EventMaskRefType <Event> )
 *  参数（输入）：
 *     TaskID		 要从其中返回事件掩码的任务
 * 参数（输出）：
 *     Event			 返回数据的内存引用
 * 特殊说明：
 *     引用的任务应当是扩展任务。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *     扩展：
 *        任务<TaskID>无效，E_OS_ID
 *        调用任务不是扩展任务，E_OS_ACCESS
 *        引用的任务<TaskID>处于挂起状态，E_OS_STATE
 * 一致性：
 *     ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) GetEvent(
	VAR(TaskType, AUTOMATIC) TaskID,
	VAR(EventMaskRefType, AUTOMATIC) Event)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
#if defined(EXTENDED_STATUS)
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK \
		| ENV_PRETASK_HOOK | ENV_POSTTASK_HOOK)

	if (TaskID >=OS_NR_TASK) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里TaskType为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}

	/**
	 * 在扩展模式下，检查任务是否为扩展任务
	 * 非扩展任务（基本任务）不支持事件，
	 * 也就不能调用本函数
	 */
	if ((osek_tasks[TaskID].flags & TF_EXTENDED) != TF_EXTENDED) {
		ret = E_OS_ACCESS;
		goto out;
	}

	/**
	 * 引用的任务处于挂起状态
	 */
	if (osek_tasks[TaskID].state == SUSPENDED) {
		ret = E_OS_STATE;
		goto out;
	}
#endif

	*Event = osek_tasks[TaskID].event_mask;

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_2(GetEvent, ret, TaskID, Event);
	}

	return ret;
}

LOCAL_INLINE FUNC(uintptr_t, OS_CODE) wait_of(
	VAR(TaskType, AUTOMATIC) TaskID)
{
	return osek_tasks[TaskID].event_wait;
}

static inline uintptr_t mask_of(TaskType TaskID)
{
	return osek_tasks[TaskID].event_mask;
}

FUNC(void, OS_CODE) __osek_set_event(
	VAR(TaskType, AUTOMATIC) TaskID,
	VAR(EventMaskType, AUTOMATIC) Mask)
{
	if ((osek_tasks[TaskID].state & (RUNNING | READY | WAITING)) != 0) {
		osek_tasks[TaskID].event_mask |= Mask;

		/**
		 * 任务正在等待事件
		 * 并且等待事件已经就绪
		 */
		if (((osek_tasks[TaskID].state & WAITING) != 0)
				&& (mask_of(TaskID) & wait_of(TaskID))) {
			/**
			 * 唤醒任务，如果在多核上面，
			 * 可能立即调度
			 * 如果在单核上面，
			 * 后面开关中断时会调度
			 */
			osek_task_wakeup(TaskID);
			osek_tasks[TaskID].event_wait &= ~mask_of(TaskID);
		}
	}
}

/**
 *  该服务可以从中断服务例程和任务级调用，但是不能从钩子例程中调用。
 *  任务<TaskID>的事件是与事件掩码<Mask>相关的集合。
 * 如果任务正在等待<Mask>指定的事件之一，
 * 那么调用SetEvent导致任务<TaskID>被转换为就绪状态。
 * 语法：
 *     StatusType SetEvent ( TaskType <TaskID>
 *          EventMaskType <Mask> )
 * 参数（输入）：
 *     TaskID	将要对其设置一个或者几个事件的任务引用
 *     Mask		要设置的事件掩码
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     未在事件掩码中设置的任何事件保持不变。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *     扩展：
 *        任务<TaskID>无效，E_OS_ID
 *        引用的任务不是扩展任务，E_OS_ACCESS
 *        由于引用任务处于挂起状态，
 *        事件不能被设置，E_OS_STATE
 * 一致性：
 *     ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) SetEvent(
	VAR(TaskType, AUTOMATIC) TaskID,
	VAR(EventMaskType, AUTOMATIC) Mask)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

#if defined(AUTOSAR_OS_APPLICATION)
	VAR(ApplicationType, AUTOMATIC) app_id;
	VAR(ApplicationType, AUTOMATIC) trusted_func_app_id;
	P2VAR(struct process_desc, AUTOMATIC, OS_APPL_DATA) pro_desc = current_proc_info();
	P2VAR(struct process_desc, AUTOMATIC, OS_APPL_DATA) target_pro_desc
		= osek_tasks[TaskID].stack;
#endif

#if defined(AUTOSAR_OS_APPLICATION)
	trusted_func_app_id = pro_desc->object.trusted_func_app_id;
	app_id = trusted_func_app_id != INVALID_OSAPPLICATION
		? trusted_func_app_id : GetApplicationID();
#endif /* defined(AUTOSAR_OS_APPLICATION) */

#if defined(EXTENDED_STATUS)
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

	if (TaskID >=OS_NR_TASK) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里TaskType为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}

	/**
	 * 在扩展模式下，检查是否有权访问app_id。
	 */
#if defined(AUTOSAR_OS_APPLICATION)
	if (CheckObjectAccess(app_id, TYPE_TASK, &target_pro_desc->object) != ACCESS) {
		ret = E_OS_ID;
		goto out;
	}
#endif /* defined(AUTOSAR_OS_APPLICATION) */
	/**
	 * 在扩展模式下，检查当前任务是否为扩展任务
	 * 非扩展任务（基本任务）不支持事件，
	 * 也就不能调用本函数
	 */
	if ((osek_tasks[TaskID].flags & TF_EXTENDED) != TF_EXTENDED) {
		ret = E_OS_ACCESS;
		goto out;
	}

	/**
	 * 如果目标任务处于挂起状态，就不能设置其事件状态
	 */
	if (osek_tasks[TaskID].state == SUSPENDED) {
		ret = E_OS_STATE;
		goto out;
	}
#endif

	SuspendAllInterrupts();

	__osek_set_event(TaskID, Mask);

	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_2(SetEvent, ret, TaskID, Mask);
	}

	return ret;
}

#if defined(AUTOSAR_OS_APPLICATION)
/*
 * 语法
 * void SetEventAsyn (
 *      TaskType id,
 *      EventMaskType m
 * )
 *
 * 服务 ID [hex]
 *      0x34
 *
 * 同步/异步
 *      异步（Asynchronous ）
 *
 * 可重入性
 *      可重入
 *
 * 参数 (in)
 *      id
 *      待激活任务的id
 *      m
 *      要设置的事件的掩码
 *
 * 参数 (inout)
 *      None
 *
 * 参数 (out)
 *      None
 *
 * 返回值
 *      None
 *
 * 描述
 *      SetEvent() 函数的异步版本。旨在用于跨核心事件设置。可能的错误不
 * 会返回给调用者，但可能会通过错误钩子报告。
 *
 * 引用
 *      AUTOSAR OS 8.4.39 SetEventAsyn [SWS_Os_91023]
 */
FUNC(void, OS_CODE) SetEventAsyn(
	VAR(TaskType, AUTOMATIC) id,
	VAR(EventMaskType, AUTOMATIC) m)
{
	VAR(uintptr_t, AUTOMATIC) flag;
	VAR(int32_t, AUTOMATIC) i;
	VAR(cpumask_t, AUTOMATIC) ipi_mask;

	CONTEXT_CHECK_NO_RETURN_VAL(ENV_TASK | ENV_CAT2_ISR)

	if (osek_tasks[id].stack == NULL) {
		return;
	}

	smp_lock_irqsave(&set_event_async_lock, flag);

	set_task_event_async_flag(&osek_tasks[id]);
	set_core_need_set_event_flag(osek_tasks[id].core_id);

	osek_tasks[id].cp_attr.async_set_event_caller[current->osek_id].event_mask = m;

	for (i = 0; i < MAX_CPUS; i++)
		cpumask_clear_cpu(i, &ipi_mask);

	cpumask_set_cpu(osek_tasks[id].core_id, &ipi_mask);

	arch_raise_ipi(&ipi_mask, IPI_ASYNC);

	smp_unlock_irqrestore(&set_event_async_lock, flag);
}
#endif /* defined(AUTOSAR_OS_APPLICATION) */

static FUNC(void, OS_CODE) __osek_wait_event(
	VAR(EventMaskType, AUTOMATIC) Mask)
{
	/**
	 * 要等待的事件已经在事件列表中
	 */
	if (Mask & osek_tasks[osek_current_id()].event_mask) {
		return;

	} else {
		osek_tasks[osek_current_id()].state = WAITING;
		/**
		* 设置想要等待的事件
		*/
		osek_tasks[osek_current_id()].event_wait |= Mask;
	}
}

/**
 * 调用任务的状态被设置为等待
 * 除非在<Mask>中指定的至少一个事件已经被设置。
 * 语法：
 *     StatusType WaitEvent ( EventMaskType <Mask> )
 * 参数（输入）：
 *     Mask			要等待的事件掩码
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     如果等待条件发生，该服务确保产生重调度。
 *     如果产生重调度，在任务处于等待状态时，
 *     任务的内部资源被释放。
 *     该服务仅仅应当在拥有事件的扩展任务中调用。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *     扩展：
 *        调用任务不是扩展任务，E_OS_ACCESS
 *        调用任务占据资源。E_OS_RESOURCE
 *        在中断级调用，E_OS_CALLEVEL
 * 一致性：
 *     ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) WaitEvent(
	VAR(EventMaskType, AUTOMATIC) Mask)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

#if defined(EXTENDED_STATUS)
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

	/**
	 * 在扩展模式下，检查任务是否为扩展任务
	 * 非扩展任务（基本任务）不支持事件，
	 * 也就不能调用本函数
	 */
	if (task_is_extended(osek_current_id())) {
		ret = E_OS_ACCESS;
		goto out;
	}

	if (OS_NR_RESOURCE > 0) { /* 系统资源数量不为0 */
		/**
		 * 任务即将退出，不应当持有资源
		 */
		if (osek_tasks[osek_current_id()].resource_count != 0) {
			ret = E_OS_RESOURCE;
			goto out;
		}
	}
#endif

	SuspendAllInterrupts();
	/**
	 * 这里真正执行等待操作
	 */
	__osek_wait_event(Mask);
	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

	if (osek_tasks[osek_current_id()].state == WAITING) {
		schedule();
	}

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(WaitEvent, ret, Mask);
	}

	return ret;
}
